#include "drv_key.h"
#include "public_datas.h"

#define TAG "KEY"

// static en_state_t key_power = DISABLE;
static const gpio_t btn_list[4] = {KEY_LOCK, KEY_UNLOCK, KEY_DOOR, KEY_USER};
static uint8_t btn_db_cnt[4] = {0, 0, 0, 0};
static uint8_t key_task_mask = 0;

static const char *string_event[] = {
    "LOCK",
    "UNLOCK",
    "OPEN_WINDOW",
    "CLOSE_WINDOW",
    "OPEN_BACK_DOOR",
    "CLOSE_BACK_DOOR",
    "FIND_CAR",
};

static const char *btn_event_str[] = {
    "CLICK",
    "LONG_PRESS",
    "DOUBLE_CLICK",
    "RELEASE"};

static const char *btn_str[] = {
    "BTN1",
    "BTN2",
    "BTN3",
    "BTN4",
};

static void key_power_set(en_state_t en)
{
    if (public_datas()->key.key_power == en)
        return;
    esay_logi(TAG, "POWER %s", string_en_status[en]);
    if (en) // 开电
    {
        gpio_set_mode(btn_list[0], GPIO_MODE_IN_FLOAT);
        gpio_set_mode(btn_list[1], GPIO_MODE_IN_FLOAT);
        gpio_set_mode(btn_list[2], GPIO_MODE_IN_FLOAT);
        gpio_set_mode(btn_list[3], GPIO_MODE_IN_FLOAT);
        gpio_set_mode(KEY_POWER, GPIO_MODE_OUT_PUSHPULL_5mA);
        gpio_set_level(KEY_POWER, 1);
        public_datas()->key.key_power = ENABLE;
    }
    else if (public_datas()->eeprom_datas.key_datas.key_always_on == DISABLE) // 关电
    {
        public_datas()->key.key_power = DISABLE;
        gpio_set_mode(KEY_POWER, GPIO_MODE_IN_PULLDOWN);
        gpio_set_mode(btn_list[0], GPIO_MODE_IN_PULLDOWN);
        gpio_set_mode(btn_list[1], GPIO_MODE_IN_PULLDOWN);
        gpio_set_mode(btn_list[2], GPIO_MODE_IN_PULLDOWN);
        gpio_set_mode(btn_list[3], GPIO_MODE_IN_PULLDOWN);
    }
}

static void drv_key_set_always_on(en_state_t en)
{
    if (public_datas()->eeprom_datas.key_datas.key_always_on == en)
        return;
    public_datas()->eeprom_datas.key_datas.key_always_on = en;
    if (en == ENABLE)
        key_power_set(ENABLE);
    else if (key_task_mask == 0)
        key_power_set(DISABLE);
}

static void key_always_on_en_cb(void *arg, size_t len)
{
    drv_key_set_always_on(ENABLE);
}

static void key_always_on_dis_cb(void *arg, size_t len)
{
    drv_key_set_always_on(DISABLE);
}

static void key_on_cb(void *arg, size_t len)
{
    key_power_set(ENABLE);
}

static void key_off_cb(void *arg, size_t len)
{
    key_power_set(DISABLE);
}

static void drv_key_set_btn_trig(key_btn_t btn, en_state_t en)
{
    esay_logi(TAG, "BTN %s num%d %s", btn_str[btn], btn_list[btn], string_en_status[en]);

    if (en) // 低电平
    {
        gpio_set_level(btn_list[btn], LOW);
        gpio_set_mode(btn_list[btn], GPIO_MODE_OUT_PUSHPULL_5mA);
    }
    else // 高电平
    {
        gpio_set_mode(btn_list[btn], GPIO_MODE_IN_FLOAT);
    }
}

static void btn_rel_cb(void *arg) // 释放IO回调
{
    key_btn_t btn = (key_btn_t)arg;
    if ((key_task_mask & (1 << btn)) == 0) // 对应按钮没有任务在忙
        return;
    // goto chek_out;

    if (btn_db_cnt[btn]) // 双击事件
    {
        if (btn_db_cnt[btn] % 2) // 奇数开io
            drv_key_set_btn_trig(btn, ENABLE);
        else // 偶数关io
            drv_key_set_btn_trig(btn, DISABLE);
        btn_db_cnt[btn]--;
        public_task_start(btn_rel_cb, (void *)btn, KEY_BTN_CLICK_TIME_MS);
        return;
    }
    esay_logi(TAG, "BTN %s release", btn_str[btn]);

    key_task_mask &= ~(1 << btn);
    drv_key_set_btn_trig(btn, DISABLE);

    // chek_out:
    //     if (key_task_mask == 0 && public_datas()->eeprom_datas.key_datas.key_always_on == DISABLE)
    //         key_power_set(DISABLE);
}

static void drv_key_set_btn_state(key_btn_t btn, key_btn_event_t state, uint8_t press_time)
{
    if (state == KEY_BTN_EVENT_RELEASE)
    {
        if ((key_task_mask & (1 << btn)) == 0) // 对应按钮没有任务在忙
            return;
        key_task_mask &= ~(1 << btn);       // 清除任务
        drv_key_set_btn_trig(btn, DISABLE); // 关闭IO
        return;
    }
    if (key_task_mask & (1 << btn)) // 对应按钮有任务在忙
    {
        return;
    }
    key_task_mask |= (1 << btn);

    uint16_t pre_ms = press_time * 1000;

    switch (state)
    {
    case KEY_BTN_EVENT_CLICK:
        drv_key_set_btn_trig(btn, ENABLE);
        pre_ms = KEY_BTN_CLICK_TIME_MS;
        break;
    case KEY_BTN_EVENT_LONG_PRESS:
        drv_key_set_btn_trig(btn, ENABLE);
        break;
    case KEY_BTN_EVENT_DOUBLE_CLICK:
        drv_key_set_btn_trig(btn, ENABLE);
        btn_db_cnt[btn] = 2;
        pre_ms = KEY_BTN_CLICK_TIME_MS;
        break;
    default:
        break;
    }
    if (pre_ms)
        public_task_start(btn_rel_cb, (void *)btn, pre_ms);
    esay_logi(TAG, "BTN %s %s %dms", btn_str[btn], btn_event_str[state], pre_ms);
}

static uint8_t get_control_id(key_event_t event)
{
    uint8_t tmp = event;
    uint8_t id = 0;
    while (tmp != 1)
    {
        tmp >>= 1;
        id++;
    }
    return id;
}

static void btn_rela_cb(void *arg) // 释放IO回调
{
    gpio_t btn = (gpio_t)arg;
    gpio_set_mode(btn, GPIO_MODE_IN_FLOAT);
}

static void drv_key_send_event(key_event_t type)
{
    uint8_t id = get_control_id(type);
    esay_logi(TAG, "event %s", string_event[id]);

    drv_key_set_btn_state(public_datas()->eeprom_datas.key_datas.btn_control[id].btn,
                          public_datas()->eeprom_datas.key_datas.btn_control[id].event,
                          public_datas()->eeprom_datas.key_datas.btn_control[id].press_time);
}

static void key_lock_cb(void *arg, size_t len)
{
    drv_key_send_event(KEY_EVENT_LOCK);
}

static void key_unlock_cb(void *arg, size_t len)
{
    drv_key_send_event(KEY_EVENT_UNLOCK);
}

static void key_open_window_cb(void *arg, size_t len)
{
    drv_key_send_event(KEY_EVENT_OPEN_WINDOW);
}

static void key_close_window_cb(void *arg, size_t len)
{
    drv_key_send_event(KEY_EVENT_CLOSE_WINDOW);
}

static void key_open_back_door_cb(void *arg, size_t len)
{
    drv_key_send_event(KEY_EVENT_OPEN_BACK_DOOR);
}

static void key_close_back_door_cb(void *arg, size_t len)
{
    drv_key_send_event(KEY_EVENT_CLOSE_BACK_DOOR);
}

static void key_find_car_cb(void *arg, size_t len)
{
    drv_key_send_event(KEY_EVENT_FIND_CAR);
}

void drv_key_install()
{
    key_power_set(DISABLE);

    esay_logi(TAG, "key always on | %s", string_en_status[public_datas()->eeprom_datas.key_datas.key_always_on]);
    if (public_datas()->eeprom_datas.key_datas.key_always_on)
    {
        key_power_set(ENABLE);
    }

    for (size_t i = 0; i < 7; i++)
    {
        if (public_datas()->eeprom_datas.key_datas.btn_control[i].event == KEY_BTN_EVENT_LONG_PRESS)
            esay_logi(TAG, "%s | %s %s %dS",
                      string_event[i],
                      btn_str[public_datas()->eeprom_datas.key_datas.btn_control[i].btn],
                      btn_event_str[public_datas()->eeprom_datas.key_datas.btn_control[i].event],
                      public_datas()->eeprom_datas.key_datas.btn_control[i].press_time);
        else
            esay_logi(TAG, "%s | %s %s",
                      string_event[i],
                      btn_str[public_datas()->eeprom_datas.key_datas.btn_control[i].btn],
                      btn_event_str[public_datas()->eeprom_datas.key_datas.btn_control[i].event]);
    }
    topic_messenger_subscribe(NULL, "key_on", key_on_cb);
    topic_messenger_subscribe(NULL, "key_off", key_off_cb);
    topic_messenger_subscribe(NULL, "key_als_on_en", key_always_on_en_cb);
    topic_messenger_subscribe(NULL, "key_als_on_dis", key_always_on_dis_cb);
    topic_messenger_subscribe(NULL, "key_lock", key_lock_cb);
    topic_messenger_subscribe(NULL, "key_unlock", key_unlock_cb);
    topic_messenger_subscribe(NULL, "key_open_wid", key_open_window_cb);
    topic_messenger_subscribe(NULL, "key_close_wid", key_close_window_cb);
    topic_messenger_subscribe(NULL, "key_open_bdoor", key_open_back_door_cb);
    topic_messenger_subscribe(NULL, "key_close_bdoor", key_close_back_door_cb);
    topic_messenger_subscribe(NULL, "key_find_car", key_find_car_cb);
}
